home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Texteditors / GoldED Tools / Warp Parser / WarpC++ / source / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-27  |  38.0 KB  |  1,321 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  C++ scanner ©1995 Dietmar Eilert
  4.  
  5.  GoldED syntax parser (uses a syntax cache). Dice:
  6.  
  7.  DMAKE
  8.  
  9.  -------------------------------------------------------------------------------
  10.  
  11. */
  12.  
  13. #include "defs.h"
  14.  
  15. /// "Header stuff"
  16.  
  17. // Buffer handles are allocated for each text buffer to keep track of ressources:
  18.  
  19. struct BufferHandle {
  20.  
  21.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  22.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  23.     struct SyntaxInfo     *bh_SyntaxInfo;            // parser output
  24.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  25. };
  26.  
  27. // known ANSI C keywords are described by syntax structures:
  28.  
  29. struct Keyword {
  30.  
  31.     UBYTE *Pattern;                                  // known pattern (e.g. "UWORD")
  32.     UBYTE *Next;                                     // valid range for next non-space char  (or NULL)
  33.     UBYTE *Required;                                 // this char has to be on the same line (or NULL)
  34.     UWORD  Level;                                    // syntax level
  35. };
  36.  
  37. #define EMPTY_STACK ((struct SyntaxInfo *)~0)        // empty stack flag
  38.  
  39. #define UPPER(a) ((a) & 95)                          // simple uppercase conversion
  40.  
  41. // syntax infos (or EMPTY_STACK) are attached to parsed lines
  42.  
  43. struct SyntaxInfo {
  44.  
  45.     UWORD              Flags;                        // comment flags
  46.     struct SyntaxChunk SyntaxChunk;                  // syntax chunks
  47. };
  48.  
  49. // these flag bits describe the line's comment properties
  50.  
  51. #define COMMENT_NONE      (0)                        // no comment in this line
  52. #define COMMENT_SIMPLE    (1L<<0)                    // there is a comment in this line
  53. #define COMMENT_START     (1L<<1)                    // line is start of multi-line comment
  54. #define COMMENT_BODY      (1L<<2)                    // line is body  of multi-line comment
  55. #define COMMENT_END       (1L<<3)                    // line is end   of multi-line comment
  56.  
  57. // macros to access SyntaxInfo members
  58.  
  59. #define GET_FLAGS(node)    (((struct SyntaxInfo *)(node)->UserData)->Flags)
  60. #define GET_CHUNK(node)   (&((struct SyntaxInfo *)(node)->UserData)->SyntaxChunk)
  61.  
  62. // recognized syntax levels
  63.  
  64. #define SYNTAX_COMMENTANSI   1
  65. #define SYNTAX_COMMENTCPP    2
  66. #define SYNTAX_RESERVED      3
  67. #define SYNTAX_PREPROCESSOR  4
  68. #define SYNTAX_NUMBER        5
  69. #define SYNTAX_STRING        6
  70. #define SYNTAX_TYPE          7
  71. #define SYNTAX_OPERATOR      8
  72. #define SYNTAX_BRACKET       9
  73. #define SYNTAX_POINTER       10
  74.  
  75. ///
  76. /// "Globals"
  77.  
  78. struct Keyword *Hash      [256];
  79. BOOL            IsOperator[256];
  80. BOOL            IsLetter  [256];
  81.  
  82. struct Keyword Reserved[] = {
  83.  
  84.     //  known_word next_char  on_same_line      level
  85.  
  86.     // uppercase
  87.  
  88.     { "APTR",       NULL,        NULL,      SYNTAX_TYPE     },
  89.     { "BOOL",       NULL,        NULL,      SYNTAX_TYPE     },
  90.     { "BPTR",       NULL,        NULL,      SYNTAX_TYPE     },
  91.     { "BPTR",       NULL,        NULL,      SYNTAX_TYPE     },
  92.     { "BYTE",       NULL,        NULL,      SYNTAX_TYPE     },
  93.     { "FLOAT",      NULL,        NULL,      SYNTAX_TYPE     },
  94.     { "LONG",       NULL,        NULL,      SYNTAX_TYPE     },
  95.     { "REGISTER",   NULL,        NULL,      SYNTAX_TYPE     },
  96.     { "STATIC",     NULL,        NULL,      SYNTAX_TYPE     },
  97.     { "STRPTR",     NULL,        NULL,      SYNTAX_TYPE     },
  98.     { "TEXT",       NULL,        NULL,      SYNTAX_TYPE     },
  99.     { "UBYTE",      NULL,        NULL,      SYNTAX_TYPE     },
  100.     { "ULONG",      NULL,        NULL,      SYNTAX_TYPE     },
  101.     { "UWORD",      NULL,        NULL,      SYNTAX_TYPE     },
  102.     { "VOID",       NULL,        NULL,      SYNTAX_TYPE     },
  103.     { "WORD",       NULL,        NULL,      SYNTAX_TYPE     },
  104.  
  105.     // lowercase
  106.  
  107.     { "break",      ";",         NULL,      SYNTAX_RESERVED },
  108.     { "case",       NULL,        ":",       SYNTAX_RESERVED },
  109.     { "catch",      "(",         NULL,      SYNTAX_RESERVED },
  110.     { "char",       NULL,        NULL,      SYNTAX_TYPE     },
  111.     { "class",      NULL,        NULL,      SYNTAX_RESERVED },
  112.     { "const",      NULL,        NULL,      SYNTAX_RESERVED },
  113.     { "continue",   ";",         NULL,      SYNTAX_RESERVED },
  114.     { "default",    ":",         NULL,      SYNTAX_RESERVED },
  115.     { "do",         NULL,        NULL,      SYNTAX_RESERVED },
  116.     { "double",     NULL,        NULL,      SYNTAX_TYPE     },
  117.     { "else",       NULL,        NULL,      SYNTAX_RESERVED },
  118.     { "enum",       NULL,        NULL,      SYNTAX_RESERVED },
  119.     { "extern",     NULL,        ";",       SYNTAX_RESERVED },
  120.     { "float",      NULL,        NULL,      SYNTAX_TYPE     },
  121.     { "for",        "(",         "(",       SYNTAX_RESERVED },
  122.     { "goto",       NULL,        NULL,      SYNTAX_RESERVED },
  123.     { "if",         "(",         "(",       SYNTAX_RESERVED },
  124.     { "inline",     NULL,        NULL,      SYNTAX_RESERVED },
  125.     { "int",        NULL,        NULL,      SYNTAX_TYPE     },
  126.     { "long",       NULL,        NULL,      SYNTAX_TYPE     },
  127.     { "private",    NULL,        NULL,      SYNTAX_RESERVED },
  128.     { "protected",  NULL,        NULL,      SYNTAX_RESERVED },
  129.     { "public",     NULL,        NULL,      SYNTAX_RESERVED },
  130.     { "register",   NULL,        NULL,      SYNTAX_RESERVED },
  131.     { "return",     "(;",        NULL,      SYNTAX_RESERVED },
  132.     { "short",      NULL,        NULL,      SYNTAX_TYPE     },
  133.     { "signed",     NULL,        NULL,      SYNTAX_TYPE     },
  134.     { "sizeof",     NULL,        NULL,      SYNTAX_RESERVED },
  135.     { "static",     NULL,        NULL,      SYNTAX_RESERVED },
  136.     { "struct",     NULL,        NULL,      SYNTAX_TYPE     },
  137.     { "switch",     "(",         NULL,      SYNTAX_RESERVED },
  138.     { "template",   "<",         NULL,      SYNTAX_RESERVED },
  139.     { "throw",      NULL,        NULL,      SYNTAX_RESERVED },
  140.     { "try",        "{",         NULL,      SYNTAX_RESERVED },
  141.     { "typedef",    NULL,        NULL,      SYNTAX_RESERVED },
  142.     { "unsigned",   NULL,        NULL,      SYNTAX_TYPE     },
  143.     { "until",      "(",         NULL,      SYNTAX_RESERVED },
  144.     { "virtual",    NULL,        NULL,      SYNTAX_RESERVED },
  145.     { "void",       NULL,        NULL,      SYNTAX_TYPE     },
  146.     { "volatile",   NULL,        NULL,      SYNTAX_RESERVED },
  147.     { "while",      "(",         NULL,      SYNTAX_RESERVED },
  148.  
  149.     { NULL, NULL, NULL, 0}
  150. };
  151.  
  152. ///
  153. /// "Prototype"
  154.  
  155. // library functions
  156.  
  157. Prototype LibCall struct ParserData     *MountScanner(void);
  158. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  159. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  160. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  161. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  162. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  163. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  164. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  165. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  166.  
  167. // private functions
  168.  
  169. Prototype void                           PrepareHash(void);
  170. Prototype struct SyntaxChunk            *ParseString(UBYTE *, UWORD, ULONG);
  171. Prototype struct SyntaxInfo             *DupInfo(struct SyntaxInfo *);
  172. Prototype struct Keyword                *IsAReservedWord(UBYTE *, UWORD);
  173. Prototype struct Keyword                *IsAReservedOperator(UBYTE *, UWORD);
  174. Prototype BOOL                           strnchr(UBYTE *, UWORD, UWORD);
  175. Prototype BOOL                           CheckEnvironment(UBYTE *, UWORD, UWORD, struct Keyword *);
  176. Prototype struct LineNode               *FindContextStart(ULONG, struct LineNode *);
  177. Prototype void                           ParseContext(struct LineNode *, ULONG, ULONG);
  178. Prototype struct SyntaxInfo             *ParseContextLine(UBYTE *, UWORD, ULONG, ULONG);
  179. Prototype BOOL                           BadSuccessor(ULONG, ULONG);
  180. Prototype struct RefreshRequest         *VerifyContext(ULONG, ULONG, ULONG);
  181.  
  182. ///
  183. /// "Library functions"
  184.  
  185. /* ------------------------------- MountScanner --------------------------------
  186.  
  187.  Called by the editor before first usage of a scanner. Return a description of
  188.  our abilities.
  189.  
  190. */
  191.  
  192. LibCall struct ParserData *
  193. MountScanner()
  194. {
  195.     static UBYTE version[] = "$VER: WarpC++ 1.4 (" __COMMODORE_DATE__ ")";
  196.  
  197.     static struct ParserData parserData;
  198.  
  199.     // syntax elements understood by parser
  200.  
  201.     static UBYTE *levelNames[] = {
  202.  
  203.         "Standard text",
  204.         "Comment ANSI",
  205.         "Comment C++",
  206.         "Reserved words",
  207.         "Preprozessor",
  208.         "Numbers",
  209.         "Strings",
  210.         "Types",
  211.         "Double operators",
  212.         "{}",
  213.         "->",
  214.  
  215.         NULL
  216.     };
  217.  
  218.     // color suggestions
  219.  
  220.     static ULONG *levelColors[] = {
  221.  
  222.         MAKE_RGB4( 0,  0,   0),                      // black
  223.         MAKE_RGB4( 5,  7,   9),                      // blue
  224.         MAKE_RGB4( 8,  8,   8),                      // grey
  225.         MAKE_RGB4(15, 15,  15),                      // white
  226.         MAKE_RGB4(15, 15,   9),                      // yellow
  227.         MAKE_RGB4( 0,  0,   0),                      // black
  228.         MAKE_RGB4( 0,  0,   0),                      // black
  229.         MAKE_RGB4( 0,  0,   0),                      // black
  230.         MAKE_RGB4( 0,  0,   0),                      // black
  231.         MAKE_RGB4(15, 15,  15),                      // white
  232.         MAKE_RGB4( 0,  0,   0),                      // black
  233.     };
  234.  
  235.     parserData.pd_Release  = SCANLIBVERSION;
  236.     parserData.pd_Version  = 1;
  237.     parserData.pd_Serial   = 0;
  238.     parserData.pd_Info     = "Warp C++ 1.4 ©'95 D.Eilert";
  239.     parserData.pd_Levels   = 11;
  240.     parserData.pd_Names    = levelNames;
  241.     parserData.pd_Colors   = levelColors;
  242.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
  243.     parserData.pd_Reserved = 0;
  244.  
  245.     PrepareHash();
  246.  
  247.     return(&parserData);
  248. }
  249.  
  250.  
  251. /* ------------------------------- StartScanner --------------------------------
  252.  
  253.  Called by the editor after a new text buffer has been created. We allocate a
  254.  buffer to hold text-specific data. The buffer address is returned as handle.
  255.  
  256. */
  257.  
  258. LibCall ULONG
  259. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
  260. {
  261.     struct BufferHandle *handle;
  262.  
  263.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  264.  
  265.         handle->bh_GlobalConfig = globalConfigPtr;
  266.         handle->bh_EditConfig   = editConfigPtr;
  267.         handle->bh_SyntaxInfo   = (struct SyntaxInfo *)syntaxStack;
  268.     }
  269.  
  270.     return((ULONG)handle);
  271. }
  272.  
  273.  
  274. /* ------------------------------- CloseScanner --------------------------------
  275.  
  276.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  277.  specific 'global' data.
  278.  
  279. */
  280.  
  281. LibCall ULONG
  282. CloseScanner(__D0 ULONG scanID)
  283. {
  284.     if (scanID) {
  285.  
  286.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  287.  
  288.         FreeVec(handle);
  289.     }
  290.  
  291.     return(0);
  292. }
  293.  
  294.  
  295. /* ------------------------------- FlushScanner --------------------------------
  296.  
  297.  Called by the editor in low memory situations: we are supposed to free as much
  298.  memory as possible.
  299.  
  300. */
  301.  
  302. LibCall void
  303. FlushScanner(__D0 ULONG scanID)
  304. {
  305.     struct BufferHandle *handle;
  306.     struct EditConfig   *config;
  307.     struct LineNode     *lineNode;
  308.  
  309.     handle = (struct BufferHandle *)scanID;
  310.     config = handle->bh_EditConfig;
  311.  
  312.     if (lineNode = config->TextNodes)
  313.         UnparseLines(lineNode, config->Lines);
  314. }
  315.  
  316.  
  317. /* ------------------------------- SetupScanner --------------------------------
  318.  
  319.  Called by the editor if the user wants to change the scanner's configuration.
  320.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  321.  unset).
  322.  
  323. */
  324.  
  325. LibCall void
  326. SetupScanner(__A0 globalConfigPtr)
  327. {
  328.     return();
  329. }
  330.  
  331.  
  332. /* ------------------------------- BriefScanner --------------------------------
  333.  
  334.  Called to notify a context scanner if lines have been added, deleted or
  335.  modified. We are supposed to return an additional display request or NULL
  336.  (the editor will refresh the damaged region only if we return NULL). Damaged
  337.  lines have already been unparsed by the editor. This is what we have to do:
  338.  
  339.  a) lines have been deleted or modified
  340.  
  341.     Check whether the syntax scheme of the remaining lines is still valid (it
  342.     will be invalid if say a comment start has been deleted but the comment
  343.     end has not been deleted). Reparse the lines and return a refresh request
  344.     if not.
  345.  
  346.  b) lines have been inserted
  347.  
  348.     Check whether the syntax sheme of the new lines affects the existing lines.
  349.     Reparse all lines starting at the insertion point and return a refresh
  350.     request if not.
  351.  
  352.  c) lines have been (un)folded
  353.  
  354.     This scanner assumes that folding doesn't affect syntax highlighting for the
  355.     sake of simplicity. This isn't necessarily a valid assumption: we will run
  356.     into touble if the user folds parts of a comment only (e.g. the first line).
  357.  
  358. */
  359.  
  360. LibCall struct RefreshRequest *
  361. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  362. {
  363.     struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  364.  
  365.     switch (notify->sn_Class) {
  366.  
  367.         case SCANNER_NOTIFY_MODIFIED:
  368.  
  369.             ULONG line = notify->sn_Line;
  370.  
  371.             if (notify->sn_Removed > notify->sn_Lines) {
  372.  
  373.                 // lines have been deleted
  374.  
  375.                 return(VerifyContext(scanID, line, 0));
  376.             }
  377.             else if (notify->sn_Lines)
  378.  
  379.                 // lines have been modified or inserted
  380.  
  381.                 return(VerifyContext(scanID, line, notify->sn_Lines));
  382.             else
  383.                 return(NULL);
  384.  
  385.             break;
  386.  
  387.         default:
  388.  
  389.             return(NULL);
  390.     }
  391. }
  392.  
  393.  
  394. /* --------------------------------- ParseLine ---------------------------------
  395.  
  396.  Parse a line, build a syntax description
  397.  
  398. */
  399.  
  400. LibCall struct SyntaxChunk *
  401. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  402. {
  403.     if (lineNode->Fold)
  404.  
  405.         return(NULL);
  406.  
  407.     else if (lineNode->Len) {
  408.  
  409.         // not yet preparsed ? preparse a couple of lines
  410.  
  411.         if (lineNode->UserData == EMPTY_STACK)
  412.  
  413.             return(NULL);
  414.  
  415.         else if (lineNode->UserData)
  416.  
  417.             return(GET_CHUNK(lineNode));
  418.  
  419.         else {
  420.  
  421.             ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line + 1;
  422.  
  423.             if (lines > 50)
  424.                 lines = 50;
  425.  
  426.             ParseSection(scanID, lineNode, lines);
  427.  
  428.             if (lineNode->UserData == EMPTY_STACK)
  429.                 return(NULL);
  430.             else
  431.                 return(GET_CHUNK(lineNode));
  432.         }
  433.     }
  434.     else
  435.         return(NULL);
  436. }
  437.  
  438.  
  439. /* -------------------------------- UnparseLines -------------------------------
  440.  
  441.  Called by the editor if lines are to be deleted. We are supposed to free
  442.  private data attached to the lines.
  443.  
  444. */
  445.  
  446. LibCall void
  447. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  448. {
  449.     while (lines--) {
  450.  
  451.         // free syntax cache
  452.  
  453.         if (lineNode->UserData) {
  454.  
  455.             if (lineNode->UserData != (APTR)EMPTY_STACK)
  456.                 FreeVec(lineNode->UserData);
  457.  
  458.             lineNode->UserData = NULL;
  459.         }
  460.  
  461.         // free folded subblock
  462.  
  463.         if (lineNode->Fold)
  464.             UnparseLines(lineNode->Fold->TextNodes, lineNode->Fold->Lines);
  465.  
  466.         ++lineNode;
  467.     }
  468. }
  469.  
  470. /* -------------------------------- ParseSection -------------------------------
  471.  
  472.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  473.  preparse the lines.
  474.  
  475. */
  476.  
  477. LibCall void
  478. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  479. {
  480.     struct LineNode *firstNode;
  481.  
  482.     if (firstNode = FindContextStart(scanID, lineNode))
  483.         ParseContext(firstNode, lines + (lineNode - firstNode), scanID);
  484. }
  485.  
  486. ///
  487. /// "private"
  488.  
  489. /* -------------------------------- PrepareHash --------------------------------
  490.  
  491.  Prepare reserved keyword hashtable (used to find keyword description if ascii
  492.  value of first letter is known) and prepare IsOperator array.
  493.  
  494. */
  495.  
  496. void
  497. PrepareHash()
  498. {
  499.     struct Keyword *keyword;
  500.     UWORD           ascii;
  501.  
  502.     memset(Hash, 0, sizeof(Hash));
  503.  
  504.     keyword = Reserved;
  505.  
  506.     while (keyword->Pattern) {
  507.  
  508.         UWORD ascii = *keyword->Pattern;
  509.  
  510.         Hash[ascii] = keyword;
  511.  
  512.         while (keyword->Pattern && (*keyword->Pattern == ascii))
  513.             ++keyword;
  514.     }
  515.  
  516.     memset(IsOperator, FALSE, sizeof(IsOperator));
  517.  
  518.     IsOperator['*'] = TRUE;
  519.     IsOperator['*'] = TRUE;
  520.     IsOperator['='] = TRUE;
  521.     IsOperator['<'] = TRUE;
  522.     IsOperator['>'] = TRUE;
  523.     IsOperator['!'] = TRUE;
  524.     IsOperator['+'] = TRUE;
  525.     IsOperator['-'] = TRUE;
  526.     IsOperator['&'] = TRUE;
  527.     IsOperator['/'] = TRUE;
  528.     IsOperator['|'] = TRUE;
  529.     IsOperator[':'] = TRUE;
  530.  
  531.     memset(IsLetter, FALSE, sizeof(IsLetter));
  532.  
  533.     for (ascii = 0; ascii <= 255; ++ascii)
  534.         IsLetter[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z'));
  535. }
  536.  
  537.  
  538.  
  539.  
  540. /* ------------------------------ IsAReservedWord ------------------------------
  541.  
  542.  Decide whether word of length <len> is a reserved ANSI C keyword
  543.  
  544. */
  545.  
  546. struct Keyword *
  547. IsAReservedWord(text, len)
  548.  
  549. UBYTE *text;
  550. UWORD  len;
  551. {
  552.     struct Keyword *keyword;
  553.  
  554.     if (keyword = Hash[*text]) {
  555.  
  556.         while (keyword->Pattern) {
  557.  
  558.             if (*keyword->Pattern > *text)
  559.  
  560.                 break;
  561.  
  562.             else if (keyword->Pattern[len] || (keyword->Pattern[1] != text[1]) || memcmp(text, keyword->Pattern, len))
  563.  
  564.                 ++keyword;
  565.             else
  566.                 return(keyword);
  567.         }
  568.  
  569.         return(NULL);
  570.     }
  571.     else
  572.         return(NULL);
  573. }
  574.  
  575.  
  576. /* ------------------------------ IsAReservedOperator --------------------------
  577.  
  578.  Decide whether word of length <len> is a reserved C 'double' operator
  579.  
  580. */
  581.  
  582. struct Keyword *
  583. IsAReservedOperator(text, len)
  584.  
  585. UBYTE *text;
  586. UWORD  len;
  587. {
  588.     static struct Keyword reserved[] = {
  589.  
  590.     //  known word    next char   on same line
  591.  
  592.         { "!=",         NULL,        NULL,      8} ,
  593.         { "&&",         NULL,        NULL,      8} ,
  594.         { "&=",         NULL,        NULL,      8} ,
  595.         { "*=",         NULL,        NULL,      8} ,
  596.         { "++",         NULL,        NULL,      8} ,
  597.         { "+=",         NULL,        NULL,      8} ,
  598.         { "--",         NULL,        NULL,      8} ,
  599.         { "-=",         NULL,        NULL,      8} ,
  600.         { "->",         NULL,        NULL,     10} ,
  601.         { "/=",         NULL,        NULL,      8} ,
  602.         { "::",         NULL,        NULL,      8} ,
  603.         { "<<",         NULL,        NULL,      8} ,
  604.         { "<=",         NULL,        NULL,      8} ,
  605.         { "==",         NULL,        NULL,      8} ,
  606.         { ">=",         NULL,        NULL,      8} ,
  607.         { ">>",         NULL,        NULL,      8} ,
  608.         { "|=",         NULL,        NULL,      8} ,
  609.         { "||",         NULL,        NULL,      8} ,
  610.         {  NULL,        NULL,        NULL,      0}
  611.     };
  612.  
  613.     struct Keyword *keyword;
  614.  
  615.     for (keyword = reserved; keyword->Pattern; ++keyword) {
  616.  
  617.         if (*keyword->Pattern > *text)
  618.  
  619.             break;
  620.  
  621.         else if ((*keyword->Pattern == *text) && (keyword->Pattern[1] == text[1]))
  622.  
  623.             return(keyword);
  624.     }
  625.  
  626.     return(NULL);
  627. }
  628.  
  629.  
  630.  
  631. /* ----------------------------- CheckEnvironment ------------------------------
  632.  
  633.  Check whether keyword environment complies with keyword.
  634.  
  635. */
  636.  
  637. BOOL
  638. CheckEnvironment(text, len, wordLen, keyword)
  639.  
  640. struct Keyword *keyword;
  641. UBYTE          *text;
  642. UWORD           len, wordLen;
  643. {
  644.     // move to first non-space character after recognized keyword
  645.  
  646.     for (text += wordLen, len -= wordLen; len && (*text == 32); ++wordLen, --len)
  647.         ++text;
  648.  
  649.     // check whether first non-space character is valid
  650.  
  651.     if ((keyword->Next == NULL) || strchr(keyword->Next, *text)) {
  652.  
  653.         // check whether required character is used on the same line
  654.  
  655.         if ((keyword->Required == NULL) || strnchr(text, len, *keyword->Required)) {
  656.  
  657.             return(TRUE);
  658.         }
  659.         else
  660.             return(FALSE);
  661.     }
  662.     else
  663.         return(FALSE);
  664. }
  665.  
  666.  
  667. /* ---------------------------------- strnchr ----------------------------------
  668.  
  669.  strchr replacement (doesn't require 0-terminated string). Return TRUE if the
  670.  character is found within <text>.
  671.  
  672. */
  673.  
  674. BOOL
  675. strnchr(text, len, character)
  676.  
  677. UBYTE *text;
  678. UWORD  character, len;
  679. {
  680.     while (len--)
  681.         if (*text++ == character)
  682.             return(TRUE);
  683.  
  684.     return(FALSE);
  685. }
  686.  
  687.  
  688. /* --------------------------------- DupInfo -----------------------------------
  689.  
  690.  Duplicate syntax info (to be FreeVec'ed)
  691.  
  692. */
  693.  
  694. struct SyntaxInfo *
  695. DupInfo(syntaxInfo)
  696.  
  697. struct SyntaxInfo *syntaxInfo;
  698. {
  699.     struct SyntaxChunk *chunk;
  700.     struct SyntaxInfo  *info;
  701.     ULONG               size;
  702.     UWORD               elements;
  703.  
  704.     // determine stack size
  705.  
  706.     for (elements = 0, chunk = &syntaxInfo->SyntaxChunk; chunk->sc_Level; ++chunk)
  707.         ++elements;
  708.  
  709.     // create copy of syntax stack (to be attached to a text line by the caller)
  710.  
  711.     size = (++elements) * sizeof(struct SyntaxChunk) + sizeof(UWORD);
  712.  
  713.     if (info = AllocVec(size + sizeof(UWORD), MEMF_PUBLIC)) {
  714.  
  715.         movmem(syntaxInfo, info, size);
  716.  
  717.         return(info);
  718.     }
  719.     else
  720.         return(NULL);
  721. }
  722.  
  723.  
  724. /*  ----------------------------- FindContextStart ------------------------------
  725.  
  726.  Search backwards until a parsed line or a context free line (fold or start of
  727.  file) is found. Return line node. This node may be used as starting point for
  728.  parsing lines.
  729.  
  730. */
  731.  
  732. struct LineNode *
  733. FindContextStart(scanID, lineNode)
  734.  
  735. ULONG            scanID;
  736. struct LineNode *lineNode;
  737. {
  738.     ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
  739.  
  740.     while (line--) {
  741.  
  742.         // found a fold or a parsed line ?
  743.  
  744.         if (lineNode->Fold || lineNode->UserData)
  745.             break;
  746.  
  747.         --lineNode;
  748.     }
  749.  
  750.     return(lineNode);
  751. }
  752.  
  753.  
  754. /* ------------------------------- ParseContext --------------------------------
  755.  
  756.  Preparse lines. The first line is expected to be either a context free line
  757.  (start of file or a fold header) or to be a parsed line.
  758.  
  759. */
  760.  
  761. void
  762. ParseContext(lineNode, lines, scanID)
  763.  
  764. struct LineNode *lineNode;
  765. ULONG            lines, scanID;
  766. {
  767.     ULONG mode;
  768.  
  769.     for (mode = COMMENT_NONE; lines--; ++lineNode) {
  770.  
  771.         if (lineNode->Fold)
  772.  
  773.             mode = COMMENT_NONE;
  774.  
  775.         else {
  776.  
  777.             if (lineNode->UserData == EMPTY_STACK)
  778.  
  779.                 mode = COMMENT_NONE;
  780.  
  781.             else if (lineNode->UserData)
  782.  
  783.                 mode = GET_FLAGS(lineNode);
  784.  
  785.             else {
  786.  
  787.                 struct SyntaxInfo *syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, mode, scanID);
  788.  
  789.                 if (syntaxInfo == EMPTY_STACK) {
  790.  
  791.                     lineNode->UserData = EMPTY_STACK;
  792.  
  793.                     mode = COMMENT_NONE;
  794.                 }
  795.                 else if (syntaxInfo) {
  796.  
  797.                     lineNode->UserData = DupInfo(syntaxInfo);
  798.  
  799.                     mode = syntaxInfo->Flags;
  800.                 }
  801.                 else
  802.                     mode = COMMENT_NONE;
  803.             }
  804.         }
  805.     }
  806. }
  807.  
  808.  
  809. /* ----------------------------- ParseContextLine ------------------------------
  810.  
  811.  Parse a string, build a syntax description. Return EMPTY_STACK in case there is
  812.  nothing to highlight. Check whether there are comments. The status of the last
  813.  line is passed in by the caller, this function will return the status (flag
  814.  bits) of the new line.
  815.  
  816. */
  817.  
  818. struct SyntaxInfo *
  819. ParseContextLine(text, len, last, scanID)
  820.  
  821. UBYTE *text;
  822. UWORD  len;
  823. ULONG  last, scanID;
  824. {
  825.     if (len) {
  826.  
  827.         struct SyntaxInfo  *syntaxInfo;
  828.         struct SyntaxChunk *syntaxStack;
  829.         struct Keyword     *keyword;
  830.  
  831.         BOOL   inString, inComment, anyText, innerComment;
  832.         UBYTE *next;
  833.         UWORD  indent, startString, startComment, element, lastChar, wordLen;
  834.         ULONG  status;
  835.  
  836.         syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  837.  
  838.         // leading spaces have to be ignored
  839.  
  840.         for (indent = 0; len && (*text == 32); ++indent, --len)
  841.             ++text;
  842.  
  843.         // trailing spaces have to be ignored
  844.  
  845.          while (len && (text[len - 1] == 32))
  846.             --len;
  847.  
  848.         // preset comment status based on status of previous line
  849.  
  850.         if (last & (COMMENT_START | COMMENT_BODY)) {
  851.  
  852.             status    = COMMENT_BODY;
  853.             inComment = TRUE;
  854.         }
  855.         else {
  856.  
  857.             status    = COMMENT_NONE;
  858.             inComment = FALSE;
  859.         }
  860.  
  861.         startComment = 0;
  862.         innerComment = FALSE;
  863.  
  864.         inString = FALSE;
  865.         anyText  = FALSE;
  866.         lastChar = 32;
  867.  
  868.         syntaxStack = &syntaxInfo->SyntaxChunk;
  869.  
  870.         for (element = 0; len; ++text, ++indent, --len) {
  871.  
  872.             if (*text != 32) {
  873.  
  874.                 if (inComment) {
  875.  
  876.                     // end of comment detected ?
  877.  
  878.                     if ((*text == '*') && (text[1] == '/') && (len > 1)) {
  879.  
  880.                         // end of a muli-line comment or end of inner line comment ?
  881.  
  882.                         if (innerComment)
  883.  
  884.                             status |= COMMENT_SIMPLE;
  885.  
  886.                         else {
  887.  
  888.                             status |=  COMMENT_END;
  889.                             status &= ~COMMENT_BODY;
  890.                         }
  891.  
  892.                         syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
  893.                         syntaxStack[element].sc_Start = startComment;
  894.                         syntaxStack[element].sc_End   = indent + 1;
  895.  
  896.                         ++element;
  897.  
  898.                         inComment = FALSE;
  899.  
  900.                         ++text;
  901.                         ++indent;
  902.                         --len;
  903.                     }
  904.                 }
  905.                 else if ((*text == 34) && ((inString == FALSE) || (*(text - 1) != 92))) {
  906.  
  907.                     if (inString = !inString)
  908.  
  909.                         startString = indent + 1;
  910.  
  911.                     else if (indent > startString) {
  912.  
  913.                         // end of string detected
  914.  
  915.                         syntaxStack[element].sc_Level = SYNTAX_STRING;
  916.                         syntaxStack[element].sc_Start = startString;
  917.                         syntaxStack[element].sc_End   = indent - 1;
  918.  
  919.                         ++element;
  920.                     }
  921.                 }
  922.                 else if (inString == FALSE) {
  923.  
  924.                     if (IsLetter[*text]) {
  925.  
  926.                         wordLen = 1;
  927.  
  928.                         while (IsLetter[text[wordLen]] && (wordLen < len))
  929.                             ++wordLen;
  930.  
  931.                         // reserved word detected ?
  932.  
  933.                         if (Hash[*text]) {
  934.  
  935.                             if ((lastChar == ' ') || (lastChar == ';') || (lastChar == '(')) {
  936.  
  937.                                 if (keyword = IsAReservedWord(text, wordLen)) {
  938.  
  939.                                     // environment to be checked ?
  940.  
  941.                                     if (keyword->Next || keyword->Required) {
  942.  
  943.                                         if (CheckEnvironment(text, len, wordLen, keyword)) {
  944.  
  945.                                             syntaxStack[element].sc_Level = keyword->Level;
  946.                                             syntaxStack[element].sc_Start = indent;
  947.                                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  948.  
  949.                                             ++element;
  950.                                         }
  951.                                     }
  952.                                     else {
  953.  
  954.                                         syntaxStack[element].sc_Level = keyword->Level;
  955.                                         syntaxStack[element].sc_Start = indent;
  956.                                         syntaxStack[element].sc_End   = indent + wordLen - 1;
  957.  
  958.                                         ++element;
  959.                                     }
  960.                                 }
  961.                             }
  962.                         }
  963.  
  964.                         // move to next section (consider end-of-loop action)
  965.  
  966.                         --wordLen;
  967.  
  968.                         text   += wordLen;
  969.                         indent += wordLen;
  970.                         len    -= wordLen;
  971.                     }
  972.                     else if ((*text >= '0') && (*text <= '9')) {
  973.  
  974.                         // number detected
  975.  
  976.                         BOOL isHex = (*text == '0') && (text[1] == 'x');
  977.  
  978.                         for (next = text + (wordLen = 1); (((*next <= '9') && (*next >= '0')) || (isHex && ((*next == 'x') || ((UPPER(*next) >= 'A') && (UPPER(*next) <= 'F'))))) && (wordLen < len); ++next)
  979.                             ++wordLen;
  980.  
  981.                         // ignore numbers attached to letters (e.g. __A0)
  982.  
  983.                         if (IsLetter[lastChar] == FALSE) {
  984.  
  985.                             syntaxStack[element].sc_Level = SYNTAX_NUMBER;
  986.                             syntaxStack[element].sc_Start = indent;
  987.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  988.  
  989.                             ++element;
  990.                         }
  991.  
  992.                         // move to next section (consider end-of-loop action)
  993.  
  994.                         --wordLen;
  995.  
  996.                         text   += wordLen;
  997.                         indent += wordLen;
  998.                         len    -= wordLen;
  999.                     }
  1000.                     else if ((*text == '/') && (text[1] == '/') && (len > 1)) {
  1001.  
  1002.                         // c++ comment detected
  1003.  
  1004.                         syntaxStack[element].sc_Level = SYNTAX_COMMENTCPP;
  1005.                         syntaxStack[element].sc_Start = indent;
  1006.                         syntaxStack[element].sc_End   = indent + len - 1;
  1007.  
  1008.                         ++element;
  1009.  
  1010.                         // terminate parsing (rest of line is a comment)
  1011.  
  1012.                         break;
  1013.                     }
  1014.                     else if ((*text == '/') && (text[1] == '*') && (len > 1)) {
  1015.  
  1016.                         // start of comment detected
  1017.  
  1018.                         inComment    = TRUE;
  1019.                         innerComment = TRUE;
  1020.                         startComment = indent;
  1021.  
  1022.                         ++text;
  1023.                         ++indent;
  1024.                         --len;
  1025.                     }
  1026.                     else if (IsOperator[*text]) {
  1027.  
  1028.                         wordLen = 1;
  1029.  
  1030.                         while (IsOperator[text[wordLen]] && (wordLen < len))
  1031.                             ++wordLen;
  1032.  
  1033.                         // reserved operator detected ?
  1034.  
  1035.                         if (keyword = IsAReservedOperator(text, wordLen)) {
  1036.  
  1037.                             syntaxStack[element].sc_Level = keyword->Level;
  1038.                             syntaxStack[element].sc_Start = indent;
  1039.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1040.  
  1041.                             ++element;
  1042.                         }
  1043.  
  1044.                         // move to next section (consider end-of-loop action)
  1045.  
  1046.                         --wordLen;
  1047.  
  1048.                         text   += wordLen;
  1049.                         indent += wordLen;
  1050.                         len    -= wordLen;
  1051.                     }
  1052.                     else if ((*text == '{') || (*text == '}')) {
  1053.  
  1054.                         // bracket detected
  1055.  
  1056.                         syntaxStack[element].sc_Level = SYNTAX_BRACKET;
  1057.                         syntaxStack[element].sc_Start = indent;
  1058.                         syntaxStack[element].sc_End   = indent;
  1059.  
  1060.                         ++element;
  1061.                     }
  1062.                     else if (*text == '#') {
  1063.  
  1064.                         // only spaces detected so far ?
  1065.  
  1066.                         if (anyText == FALSE) {
  1067.  
  1068.                             // preprocessor line detected
  1069.  
  1070.                             wordLen = 1;
  1071.  
  1072.                             while ((wordLen < len) && (text[wordLen] == 32))
  1073.                                 ++wordLen;
  1074.  
  1075.                             // get preprocessor command only
  1076.  
  1077.                             while ((wordLen < len) && (text[wordLen] != 32)) {
  1078.  
  1079.                                 if ((text[wordLen] == '/') && (wordLen + 1 < len) && (text[wordLen + 1] == '/'))
  1080.                                     break;
  1081.                                 else
  1082.                                     ++wordLen;
  1083.                             }
  1084.  
  1085.                             syntaxStack[element].sc_Level = SYNTAX_PREPROCESSOR;
  1086.                             syntaxStack[element].sc_Start = indent;
  1087.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1088.  
  1089.                             // move to end of reserved word (consider end-of-loop action)
  1090.  
  1091.                             --wordLen;
  1092.  
  1093.                             text   += wordLen;
  1094.                             indent += wordLen;
  1095.                             len    -= wordLen;
  1096.  
  1097.                             ++element;
  1098.                         }
  1099.                     }
  1100.                 }
  1101.  
  1102.                 anyText = TRUE;
  1103.             }
  1104.  
  1105.             lastChar = *text;
  1106.         }
  1107.  
  1108.         // comment not closed ?
  1109.  
  1110.         if (inComment) {
  1111.  
  1112.             // continuation of multi-line comment or new (inner line) comment ?
  1113.  
  1114.             if (innerComment)
  1115.                 status |= COMMENT_START;
  1116.             else
  1117.                 status |= COMMENT_BODY;
  1118.  
  1119.             syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
  1120.             syntaxStack[element].sc_Start = startComment;
  1121.             syntaxStack[element].sc_End   = indent - 1;
  1122.  
  1123.             ++element;
  1124.         }
  1125.  
  1126.         if (element) {
  1127.  
  1128.             // terminate syntax stack
  1129.  
  1130.             syntaxStack[element].sc_Start = FALSE;
  1131.             syntaxStack[element].sc_End   = FALSE;
  1132.             syntaxStack[element].sc_Level = FALSE;
  1133.  
  1134.             syntaxInfo->Flags = status;
  1135.  
  1136.             return(syntaxInfo);
  1137.         }
  1138.         else
  1139.             return(EMPTY_STACK);
  1140.     }
  1141.     else if (last) {
  1142.  
  1143.         if (last & (COMMENT_START | COMMENT_BODY)) {
  1144.  
  1145.             struct SyntaxInfo *syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  1146.  
  1147.             syntaxInfo->SyntaxChunk.sc_Start = FALSE;
  1148.             syntaxInfo->SyntaxChunk.sc_End   = FALSE;
  1149.             syntaxInfo->SyntaxChunk.sc_Level = FALSE;
  1150.  
  1151.             syntaxInfo->Flags = COMMENT_BODY;
  1152.  
  1153.             return(syntaxInfo);
  1154.         }
  1155.         else
  1156.             return(EMPTY_STACK);
  1157.     }
  1158.     else
  1159.         return(EMPTY_STACK);
  1160. }
  1161.  
  1162.  
  1163. /* ------------------------------- VerifyContext -------------------------------
  1164.  
  1165.  Ensure that syntax information of a specified range of lines ("damage region")
  1166.  is valid and consistent with the existing text. Return a refresh request if
  1167.  not. The damage area is expected to have been unparsed already. This is what we
  1168.  have to do: we preparse existing lines before the damage area if belonging to
  1169.  the syntax context of the damage area (ie. all lines affecting highlighting of
  1170.  the first line in the damage area). The damage area is parsed, too. Parsed
  1171.  lines after the damage area are reparsed if highlighting is found to be
  1172.  inconsistent with the line(s) before. Reparsing continues until the end of the
  1173.  file respectively until no more inconsistencies are found.
  1174.  
  1175. */
  1176.  
  1177. struct RefreshRequest *
  1178. VerifyContext(scanID, line, lines)
  1179.  
  1180. ULONG scanID, line, lines;
  1181. {
  1182.     struct EditConfig *config;
  1183.     struct LineNode   *lineNode, *lastNode;
  1184.     struct SyntaxInfo *syntaxInfo;
  1185.  
  1186.     ULONG last, new, refreshStart, refresh = FALSE;
  1187.  
  1188.     config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  1189.  
  1190.     lineNode = config->TextNodes + line;
  1191.     lastNode = config->TextNodes + line + lines - 1;
  1192.  
  1193.     // preparse from context start until end of damage area
  1194.  
  1195.     ParseSection(scanID, lineNode, lines);
  1196.  
  1197.     // get syntax flags of last line in damage area
  1198.  
  1199.     if (lastNode->Fold || (lastNode->UserData == EMPTY_STACK) || (lastNode->UserData == NULL))
  1200.         last = COMMENT_NONE;
  1201.     else
  1202.         last = GET_FLAGS(lastNode);
  1203.  
  1204.     // continue parsing until no more inconsistencies are found (until context end)
  1205.  
  1206.     refreshStart = (line += lines);
  1207.  
  1208.     for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
  1209.  
  1210.         if (lineNode->Fold)
  1211.  
  1212.             // folds terminate parsing (context end)
  1213.  
  1214.             break;
  1215.  
  1216.         else if (lineNode->UserData == NULL) {
  1217.  
  1218.             // line not yet parsed
  1219.  
  1220.             syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1221.  
  1222.             if (syntaxInfo == EMPTY_STACK)
  1223.  
  1224.                 lineNode->UserData = EMPTY_STACK;
  1225.  
  1226.             else if (syntaxInfo)
  1227.  
  1228.                 lineNode->UserData = DupInfo(syntaxInfo);
  1229.         }
  1230.         else {
  1231.  
  1232.             // check whether highlighting of this line is consistent with previous line
  1233.  
  1234.             new = (lineNode->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(lineNode);
  1235.  
  1236.             if (BadSuccessor(last, new)) {
  1237.  
  1238.                 if (lineNode->UserData != EMPTY_STACK)
  1239.                     FreeVec(lineNode->UserData);
  1240.  
  1241.                 syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1242.  
  1243.                 if (syntaxInfo == EMPTY_STACK)
  1244.  
  1245.                     lineNode->UserData = EMPTY_STACK;
  1246.  
  1247.                 else if (syntaxInfo)
  1248.  
  1249.                     lineNode->UserData = DupInfo(syntaxInfo);
  1250.             }
  1251.             else
  1252.                 break;
  1253.         }
  1254.  
  1255.         if (lineNode->Fold || (lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL))
  1256.             last = COMMENT_NONE;
  1257.         else
  1258.             last = GET_FLAGS(lineNode);
  1259.     }
  1260.  
  1261.     if (refresh) {
  1262.  
  1263.         struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
  1264.  
  1265.         refreshRequest->rr_Line  = refreshStart;
  1266.         refreshRequest->rr_Lines = refresh;
  1267.  
  1268.         return(refreshRequest);
  1269.     }
  1270.     else
  1271.         return(NULL);
  1272. }
  1273.  
  1274.  
  1275. /* ------------------------------- BadSuccessor --------------------------------
  1276.  
  1277.  Return TRUE if syntax information of two adjacent lines is inconsistent.
  1278.  
  1279. */
  1280.  
  1281. BOOL
  1282. BadSuccessor(pred, succ)
  1283.  
  1284. ULONG pred, succ;
  1285. {
  1286.     if (succ & (COMMENT_BODY | COMMENT_END)) {
  1287.  
  1288.         // comment body/end without start ?
  1289.  
  1290.         if ((pred & (COMMENT_START | COMMENT_BODY)) == FALSE)
  1291.  
  1292.             return(TRUE);
  1293.     }
  1294.     else if (pred & COMMENT_START) {
  1295.  
  1296.         // comment start without body/end ?
  1297.  
  1298.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1299.  
  1300.             return(TRUE);
  1301.  
  1302.         // comment inside comment ?
  1303.  
  1304.         if (succ & COMMENT_START)
  1305.  
  1306.             return(TRUE);
  1307.     }
  1308.     else if (pred & COMMENT_BODY) {
  1309.  
  1310.         // body line without end ?
  1311.  
  1312.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1313.  
  1314.             return(TRUE);
  1315.     }
  1316.  
  1317.     return(FALSE);
  1318. }             
  1319.  
  1320. ///
  1321.